package com.ciaojian.client.util;


import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Predicate;

/**
 * 数组操作工具类
 *
 * @author
 */
public class ArrayUtil {

    public static final String[] EMPTY_STRING_ARRAY = new String[0];

    /**
     * 避免不小心在类的内部调用构造器. 保证该类在任何情况下都不会被实例化.
     */
    private ArrayUtil() {
    }

    /**
     * 新建数组
     *
     * @param type
     * @param size
     * @return
     */
    public static <T> T[] newArray(Class<?> type, int size) {
        return (T[]) Array.newInstance(type, size);
    }

    /**
     * 组合元素到数组
     *
     * @param elements
     * @return
     */
    public static <T> T[] array(final T... elements) {
        return elements;
    }

    /**
     * 数组是否为空<br>
     * 判断集合是否为{@code null}，或者元素个数是否等于0
     *
     * @param array 数组
     * @return 是否为空
     */
    public static <T> boolean isEmpty(final T[] array) {
        return array == null || array.length <= 0;
    }

    /**
     * 数组是否为非空
     *
     * @param array 数组
     * @return 是否为非空
     */
    public static <T> boolean isNotEmpty(final T[] array) {
        return false == isEmpty(array);
    }

    /**
     * 判定给定对象是否为数组类型
     *
     * @param obj 对象
     * @return 是否为数组类型
     */
    public static boolean isArray(final Object obj) {
        return obj.getClass().isArray();
    }

    /**
     * 生成数组<br>
     * 格式为：perfix+i<br>
     * 例如：prefix传入leopard,数量传入3;<br>
     * 返回的数组为：["leopard0","leopard1","leopard2"]
     *
     * @param prefix 数组内容前缀
     * @param count  数量
     * @return 生成的数组
     */
    public static String[] make(final String prefix, final int count) {
        String[] members = new String[count];
        for (int i = 0; i < members.length; i++) {
            members[i] = prefix + i;
        }
        return members;
    }

    /**
     * 查找元素在数组中的位置
     *
     * @param array
     * @param value
     * @return
     */
    public static int indexOf(final Object[] array, final Object value) {
        for (int i = 0; i < array.length; i++) {
            if (array[i] == value || array[i].equals(value)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 检查数组是否含有某元素
     *
     * @param array
     * @param value
     * @return
     */
    public static boolean contains(final Object[] array, final Object value) {
        return indexOf(array, value) != -1;
    }

    /**
     * 以逗号分隔将数组拼凑成字符串
     *
     * @param array
     * @param separator
     * @return
     */
    public static String join(final Object[] array, final String separator) {
        if (array == null) {
            return Consts.EMPTY;
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < array.length; i++) {
            if (i != 0) {
                if (separator == null) {
                    sb.append(Consts.COMMA);
                }
            }
            sb.append(array[i]);
        }
        return sb.toString();
    }

    /**
     * 调整数组元素个数（截取数组一部分）
     *
     * @param buffer
     * @param newSize
     * @return
     */
    public static <T> T[] resize(final T[] buffer, final int newSize) {
        Class<T> componentType = (Class<T>) buffer.getClass().getComponentType();
        T[] temp = (T[]) Array.newInstance(componentType, newSize);
        System.arraycopy(buffer, 0, temp, 0, buffer.length >= newSize ? newSize : buffer.length);
        return temp;
    }

    /**
     * 往数组里追加元素
     *
     * @param buffer
     * @param newElement
     * @return
     */
    public static <T> T[] append(final T[] buffer, final T newElement) {
        T[] t = resize(buffer, buffer.length + 1);
        t[buffer.length] = newElement;
        return t;
    }

    /**
     * 移除数组中的元素
     *
     * @param buffer
     * @param offset
     * @param length
     * @param componentType
     * @return
     */
    public static <T> T[] remove(final T[] buffer, final int offset, final int length, final Class<T> componentType) {
        int len2 = buffer.length - length;
        T[] temp = (T[]) Array.newInstance(componentType, len2);
        System.arraycopy(buffer, 0, temp, 0, offset);
        System.arraycopy(buffer, offset + length, temp, offset, len2 - offset);
        return temp;
    }

    /**
     * 移除数组中的元素
     *
     * @param buffer
     * @param offset
     * @param length
     * @return
     */
    public static <T> T[] remove(final T[] buffer, final int offset, final int length) {
        Class<T> componentType = (Class<T>) buffer.getClass().getComponentType();
        return remove(buffer, offset, length, componentType);
    }

    /**
     * 截取数组
     *
     * @param buffer
     * @param offset
     * @param length
     * @param componentType
     * @return
     */
    public static <T> T[] subarray(final T[] buffer, final int offset, final int length, final
    Class<T> componentType) {
        T[] temp = (T[]) Array.newInstance(componentType, length);
        System.arraycopy(buffer, offset, temp, 0, length);
        return temp;
    }

    /**
     * 截取数组
     *
     * @param buffer
     * @param offset
     * @param length
     * @return
     */
    public static <T> T[] subarray(final T[] buffer, final int offset, final int length) {
        Class<T> componentType = (Class<T>) buffer.getClass().getComponentType();
        return subarray(buffer, offset, length, componentType);
    }

    /**
     * 往数组插入元素
     *
     * @param dest
     * @param src
     * @param offset
     * @param componentType
     * @return
     */
    public static <T> T[] insert(final T[] dest, final T[] src, final int offset, final Class<?> componentType) {
        T[] temp = (T[]) Array.newInstance(componentType, dest.length + src.length);
        System.arraycopy(dest, 0, temp, 0, offset);
        System.arraycopy(src, 0, temp, offset, src.length);
        System.arraycopy(dest, offset, temp, src.length + offset, dest.length - offset);
        return temp;
    }

    /**
     * 往数组插入元素
     *
     * @param dest
     * @param src
     * @param offset
     * @return
     */
    public static <T> T[] insert(final T[] dest, final T[] src, final int offset) {
        Class<T> componentType = (Class<T>) dest.getClass().getComponentType();
        return insert(dest, src, offset, componentType);
    }

    /**
     * 往数组插入元素<br>
     * 添加新元素会生成一个新的数组，不影响原数组
     *
     * @param dest
     * @param src
     * @param offset
     * @param componentType
     * @return
     */
    public static <T> T[] insert(final T[] dest, final T src, final int offset, final Class<?> componentType) {
        T[] temp = (T[]) Array.newInstance(componentType, dest.length + 1);
        System.arraycopy(dest, 0, temp, 0, offset);
        temp[offset] = src;
        System.arraycopy(dest, offset, temp, offset + 1, dest.length - offset);
        return temp;
    }

    /**
     * 往数组插入元素
     *
     * @param dest
     * @param src
     * @param offset
     * @return
     */
    public static <T> T[] insert(final T[] dest, final T src, final int offset) {
        Class<T> componentType = (Class<T>) dest.getClass().getComponentType();
        return insert(dest, src, offset, componentType);
    }

    /**
     * 将新元素添加到已有数组中<br>
     * 添加新元素会生成一个新的数组，不影响原数组
     *
     * @param <T>         数组元素类型
     * @param src         已有数组
     * @param newElements 新元素
     * @return 新数组
     */
    @SafeVarargs
    public static <T> T[] append(final T[] src, final T... newElements) {
        if (isEmpty(newElements)) {
            return src;
        }
        return insert(newElements, src, 0);
    }

    /**
     * 两个数组合成map<br>
     * 映射键值（参考Python的zip()函数）<br>
     * 例如：<br>
     * keys = [a,b,c,d]<br>
     * values = [1,2,3,4]<br>
     * 则得到的Map是 {a=1, b=2, c=3, d=4}<br>
     * 如果两个数组长度不同，则只对应最短部分
     *
     * @param keys   键列表
     * @param values 值列表
     * @return Map
     */
    public static <T, K> Map<T, K> zip(final T[] keys, final K[] values) {
        if (isEmpty(keys) || isEmpty(values)) {
            return null;
        }

        final int size = Math.min(keys.length, values.length);
        final Map<T, K> map = new HashMap<T, K>((int) (size / 0.75));
        for (int i = 0; i < size; i++) {
            map.put(keys[i], values[i]);
        }

        return map;
    }

    /**
     * 反转数组
     *
     * @param array
     * @return
     */
    public static <T> T[] reverse(T[] array) {
        if (isEmpty(array)) {
            return array;
        }
        T[] newArray = array.clone();
        for (int i = 0; i < newArray.length; i++) {
            newArray[i] = array[array.length - i - 1];
        }
        return newArray;
    }

    /**
     * 通过传入的Predicate过滤返回需要的元素内容
     *
     * @param array
     * @param predicate
     * @param <T>
     * @return
     */
    public static <T> T[] filter(T[] array, Predicate<T> predicate) {
        if (null == predicate) {
            return array;
        }

        final ArrayList<T> list = new ArrayList<T>(array.length);
        for (T t : array) {
            if (predicate.test(t)) {
                list.add(t);
            }
        }
        final T[] result = newArray(array.getClass().getComponentType(), list.size());
        return list.toArray(result);
    }

    /**
     * 强转数组类型<br>
     * 强制转换的前提是数组元素类型可被强制转换<br>
     * 强制转换后会生成一个新数组
     *
     * @param objArray 原数组
     * @param type     强转的数组元素类型
     * @return
     */
    public static <T> T[] cast(Object[] objArray, Class<T> type) {
        if (ArrayUtil.isEmpty(objArray) || null == type) {
            return (T[]) objArray;
        }

        final Object[] array = (Object[]) objArray;
        final T[] result = newArray(type, array.length);
        System.arraycopy(array, 0, result, 0, array.length);
        return result;
    }

    /**
     * 字符串集合对象转为int数组
     *
     * @param strArr
     * @return
     */
    public static Integer[] toIntArray(String[] strArr) {
        if (strArr == null) {
            return null;
        }
        Integer[] arr = new Integer[strArr.length];
        for (int i = 0; i < strArr.length; i++) {
            arr[i] = Conver.toInt(strArr[i]);
        }
        return arr;
    }

}
